

// in order to get function prototypes from glext.h, 
// define GL_GLEXT_PROTOTYPES before including glext.h
#define GL_GLEXT_PROTOTYPES

#include <GL/gl.h>
#include <GL/glext.h>

#include <string>

#include "test_disp.h"
#include "../types.h"

const char* test_disp::text[] =
{
    "Load a LOD hierarchy from multi-ply file",
    "Load a LOD hierarchy from:  ",
};

const int test_disp::yinc_text[] =
{
    0, // both
    0, // rely on test_disp::reshape()
};

const int test_disp::xoffset_text[] = 
{
    0,
    0,
};

bool test_disp::show_text[] =
{
    true,
    false,
};

bool test_disp::menu_pos[] =
{
    true,   // these two are
    false,   // alternatives
};

const int test_disp::text_count = 2;



const short test_disp::START_ACT_MENU_POS = 0;
//const bool test_disp::HELP_SHOWED_ON_START = false;



const comp_type test_disp::DEFAULT_MAT_DIFF[] = {0.6, 0.3, 0.9, 1.0};
const comp_type test_disp::DEFAULT_MAT_SPEC[] = {0.3, 0.3, 0.3, 1.0};



const char* test_disp::help_str[] =
{
    "escape - go to the main menu",
    "  u    - use/don't use LOD mechanism",
    "  ]    - add a random object",
    "  [    - delete a random object",
    "  p    - pause/play",
    "",
    "  b    - draw wire balls around objects",
    "  f    - show wireframe/solid",
    "  S    - smooth/flat shading",
    "  l    - turn on/off the lights",
    "  d    - display/hide the object",
    "",
    "  v    - turn on/off VBO",
    "  c    - turn on/off back-face culling",
    "  n    - print the number of objects and triangles",
    "",
    "with LMB pressed mouse movement rotates the scene",
    "with RMB pressed mouse movement rescales the scene",
    //" w/up  - go up in the menu",
    //"s/down - go down in the menu",
};

const int test_disp::help_count = 18;



void test_disp::load_multi_ply(std::string filename)
{
    jeff::Mesh* mesh;
    int num=0;
    bool more=true;
    long offset=0;

    // delete old meshes, if any
    unsigned end=meshes.size();
    for(unsigned i=0; i<end; i++) {
        //if(meshes[i]!=NULL)
            delete meshes[i];
    }
    meshes.clear();

    end=vbo_ids.size();
    if(end>0)
        // delete old vertex buffers
        glDeleteBuffers(end, &(vbo_ids[0]));

    while(more) {
                                                // offset in a multi-ply file
        mesh=new jeff::Mesh((char*)filename.c_str(),offset);
        if(mesh->getNumVerts() == 0) { // we ain't got anything
            delete mesh;
            more=false;
        }
        else {
            meshes.push_back(mesh);
            offset=mesh->getFileOffset();
            num++;
            mesh->Normalize();
        }
    }

    cout<<"Loaded "<<num<<" LODs"<<endl<<flush;
    for(int i=0; i<num; i++)
        cout<<"LOD "<<i<<": "<<meshes[i]->getNumVerts()<<" verts, "
            <<meshes[i]->getNumTriangles()<<" triangles"<<endl;
    cout<<flush;

    if(num>0) {
        ob_loaded=true;
        ob_name=filename;
        display_ob=true;
        display_on();
        use_vbo=true;
        illuminated=true;
        polygon_mode=true; // filled polys
        set_pause(false);
        draw_balls=false;
        use_lod=true;
        show_help=false;

        //li.set_smooth_shading();
        li.set_flat_shading();
        jcam.resetOrientation();
        jcam.set_dist(15);
        set_box();
        generate_pyramid();

        histogram.resize(meshes.size());
        
        //  VBO
                    // for vertices, their normals, and for triangles
        vbo_ids.resize(2*num);
        //for(int k=0; k<vbo_ids.size(); k++) cout<<vbo_ids[k]<<"  ";
        //cout<<endl<<flush;
        glGenBuffers(2*num, &(vbo_ids[0]));
        //for(int k=0; k<vbo_ids.size(); k++) cout<<vbo_ids[k]<<"  ";
        //cout<<endl<<flush;

        for(int i=0; i<num; i++) 
        {
            int t_indices[3];
            GLsizei size=3*sizeof(float);
            GLint offset=0;
            mesh = meshes[i];
            int end=mesh->getNumVerts();

            // vertices
            glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo_ids[i]);
                                            // vertex and normal -> same sizes
            glBufferData(GL_ARRAY_BUFFER_ARB, 2*size*end, 
                         NULL, GL_STATIC_DRAW_ARB);
            for(int j=0; j<end; j++) {
                
                //glBufferData(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB);
                glBufferSubData(GL_ARRAY_BUFFER_ARB, offset, 
                                size, mesh->getVertex(j).getArrayVerts());
                offset+=size;
            }

            //offset=0;
            // normals
            //glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo_ids[num+i]);
            //glBufferData(GL_ARRAY_BUFFER_ARB, size*end,
            //             NULL, GL_STATIC_DRAW_ARB);
            for(int j=0; j<end; j++) {
                glBufferSubData(GL_ARRAY_BUFFER_ARB, offset, 
                                size, mesh->getVertex(j).getArrayVertNorms());
                offset+=size;
            }

            end=mesh->getNumTriangles();
            offset=0;
            size=3*sizeof(int);
            // triangles; each triangle is given as 3 indices pointing to vertex buffer
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_ids[num+i]);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, size*end, 
                         NULL, GL_STATIC_DRAW_ARB);
            for(int j=0; j<end; j++) {
                mesh->getTri(j).getVerts(t_indices[0], t_indices[1], t_indices[2]);
                glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset,
                                size,t_indices);
                offset+=size;
            }
        }

        // switch off VBO
        glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }
    else {
        ob_loaded=false;
        ob_name.clear();
        display_off();
        ballbox.clear();
        pts.clear();
        clear_pyramid();

        histogram.clear();
    }
    reshape(width,height);
}



test_disp::~test_disp()
{
    unsigned end=meshes.size();
    for(unsigned i=0; i<end; i++)
        //if(meshes[i]!=NULL)
            delete meshes[i];

    // it's not the best place to do that but it should work fine
    if(end>0)
        // delete old vertex buffers
        glDeleteBuffers(end, &(vbo_ids[0]));
}


void test_disp::display_jmesh(jeff::Mesh* mesh, int idx)
{
    assert(mesh!=NULL);
    if(use_vbo==false)
    {
        glBegin(GL_TRIANGLES);
        int end=mesh->getNumTriangles();
        for (int i=0; i<end; i++)
        {
            jeff::triangle& t=mesh->getTri(i);
            float a[3];
            
            if (li.is_smooth_shading())
            {
                    const jeff::vertex& v1 = t.getVert1vertex();
                    const float* pFltV1Norm = v1.getArrayVertNorms();
                    glNormal3fv(pFltV1Norm);
                    const float* pFltArray1 = v1.getArrayVerts();
                    glVertex3fv(pFltArray1);
                    const jeff::vertex& v2 = t.getVert2vertex();
                    const float* pFltV2Norm = v2.getArrayVertNorms();
                    glNormal3fv(pFltV2Norm);
                    const float* pFltArray2 = v2.getArrayVerts();
                    glVertex3fv(pFltArray2);
                    const jeff::vertex& v3 = t.getVert3vertex();
                    const float* pFltV3Norm = v3.getArrayVertNorms();
                    glNormal3fv(pFltV3Norm);
                    const float* pFltArray3 = v3.getArrayVerts();
                    glVertex3fv(pFltArray3);

                    // flip it
                    a[0] = -pFltV3Norm[0];
                    a[1] = -pFltV3Norm[1];
                    a[2] = -pFltV3Norm[2];
                    glNormal3fv(a);
                    glVertex3fv(pFltArray3);
                    a[0] = -pFltV2Norm[0];
                    a[1] = -pFltV2Norm[1];
                    a[2] = -pFltV2Norm[2];
                    glNormal3fv(a);
                    glVertex3fv(pFltArray2);
                    a[0] = -pFltV1Norm[0];
                    a[1] = -pFltV1Norm[1];
                    a[2] = -pFltV1Norm[2];
                    glNormal3fv(a);
                    glVertex3fv(pFltArray1);
            }
            else // flat shaded
            {
                    const float* pFltArrayN = t.getNormal();
                    glNormal3fv(pFltArrayN);
                    const float* pFltArray1 = t.getVert1();
                    glVertex3fv(pFltArray1);
                    const float* pFltArray2 = t.getVert2();
                    glVertex3fv(pFltArray2);
                    const float* pFltArray3 = t.getVert3();
                    glVertex3fv(pFltArray3);

                    a[0] = -pFltArrayN[0];
                    a[1] = -pFltArrayN[1];
                    a[2] = -pFltArrayN[2];
                    glNormal3fv(a);
                    glVertex3fv(pFltArray3);
                    glVertex3fv(pFltArray2);
                    glVertex3fv(pFltArray1);
            }
        }
        glEnd();
    }
    else // use vbo
    {
        if(mesh->getNumTriangles()>0) {
            int num=meshes.size();
            assert((int)vbo_ids.size()==2*num);

            glBindBuffer(GL_ARRAY_BUFFER_ARB, vbo_ids[idx]);
            glVertexPointer(3, GL_FLOAT, 0, 0);
            glNormalPointer(GL_FLOAT, 0, (GLvoid*)(3*sizeof(float)*mesh->getNumVerts()));

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_ids[num+idx]);

            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_NORMAL_ARRAY);


            glDrawElements(GL_TRIANGLES, 3*mesh->getNumTriangles(), GL_UNSIGNED_INT, 0);

            glDisableClientState(GL_VERTEX_ARRAY);
            glDisableClientState(GL_NORMAL_ARRAY);
            
            glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
    }
}
